home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS05.ADF / demos / sweep.c < prev   
C/C++ Source or Header  |  1986-01-15  |  9KB  |  281 lines

  1.  
  2. /* sweep.c AmigaLink 1/25/86 */
  3. /*****************************************************************************
  4.  *
  5.  * Double-Buffered Sound Synthesis Example
  6.  *
  7.  * Sam Dicker
  8.  * 6 December 1985
  9.  * (created:  8 October 1985)
  10.  *
  11.  * This program the demonstrates double-buffered writing to an audio channel
  12.  * using the the hardware control commands.  This technique can be used to
  13.  * synthesize sound in 'real-time'.  This program uses the mouse as a simple
  14.  * input device and to keep the example simple, directly reads the mouse
  15.  * register.
  16.  *
  17.  * Real-time synthesis code should always be written in the fastest assembly
  18.  * language possible (unlike this example) and should try to pre-compute as
  19.  * much data as possible.  In this example, a sine wave lookup table is
  20.  * pre-computed.  Then, while the sound is being played, the table is scanned
  21.  * at a rate dependent on a variable (frequency) and the scanned values are
  22.  * copied into temporary buffers.  This frequency variable is modified by
  23.  * mouse movement, effectively making the mouse a pitch control.  In a 'real'
  24.  * program, since pitch is the only parameter being controlled, it would be
  25.  * much more efficient to use modify the 'period' and play one fixed sine wave<  * waveform buffer (or one waveform for each octave).
  26.  *
  27.  * Two temporary buffers are used. One must be computed and sent to the audio
  28.  * device before the other one has finished playing; otherwise, the audio
  29.  * device turns off the sound making a pop.  This program runs in software
  30.  * interrupts to insure that it gets adequate processor time to avoid this
  31.  * problem.
  32.  *
  33.  *****************************************************************************/
  34.  
  35. #include "exec/types.h"
  36. #include "exec/memory.h"
  37. #include "exec/interrupts.h"
  38. #include "exec/errors.h"
  39. #include "hardware/custom.h"
  40. #include "libraries/dos.h"
  41. #include "devices/audio.h"
  42.  
  43. #define BUFFERSIZE  250
  44. #define SINETABLEPOWER2       10
  45. #define SINETABLESIZE (1 << SINETABLEPOWER2)
  46. #define SINETABLESTEP (2 * 3.141593 / SINETABLESIZE)
  47.  
  48. /* mouse register addresses */
  49. #define XMOUSEREG   (*((BYTE *)&joy0dat + 1))
  50. #define YMOUSEREG   (-(*(BYTE *)&joy0dat))
  51.  
  52. extern struct MsgPort *CreatePort();
  53. extern struct Library *OpenLibrary();
  54. extern struct Task *FindTask();
  55. extern UWORD joy0dat;
  56.  
  57. /* channel allocation map */
  58. UBYTE allocationMap[] = { 1, 8, 2, 4 };
  59.  
  60. struct Library *MathBase = 0;    /* used by cleanUp to determine
  61.                         * what needs to be 'cleaned up' */
  62. struct MsgPort *allocPort = 0;
  63. struct IOAudio *allocIOB = 0;
  64. struct Device *device = 0;
  65. struct Interrupt *interrupt = 0;
  66. struct MsgPort *soundPort = 0;
  67. BYTE *buffer[2] = { 0 };
  68. struct IOAudio *soundIOB[2] = { 0 };
  69.  
  70. int newBuffer();
  71. UBYTE sineTable[SINETABLESIZE];
  72. ULONG angle = 0;
  73. ULONG frequency = 0x2000000;
  74. BYTE lastYMouse;
  75.  
  76.  
  77. main()
  78. {
  79.     int i;
  80.     FLOAT sine, cosine;
  81.  
  82.     /* open the math library */
  83.  
  84.     if ((MathBase = OpenLibrary("mathffp.library", 0)) == 0)
  85.      cleanUp("Cannot open math library");
  86.  
  87.     /* generate the sine lookup table.  The table could have been computed by
  88.      * calling the 'sin' function for each point, but this method is a little
  89.      * faster where great accuracy is not required */
  90.  
  91.     for (i = 0, cosine = (sine = 0.0) + 1.0; i < SINETABLESIZE;
  92.          ++i, sine += SINETABLESTEP * (cosine -= SINETABLESTEP * sine)) {
  93.  
  94.      /* generate table values between -128 and 127 */
  95.      sineTable[i] = 127 * sine + 0.5;
  96.  
  97.     }
  98.  
  99.     /* read the starting mouse count */
  100.     lastYMouse = YMOUSEREG;
  101.  
  102.     /* initialize I/O block to allocate a channel when the audio device is
  103.      * OpenDevice'd */
  104.  
  105.     if ((allocPort = CreatePort("sound example", 0)) == 0)
  106.      cleanUp("Cannot create reply port");
  107.     if ((allocIOB = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),
  108.          MEMF_PUBLIC | MEMF_CLEAR)) == 0)
  109.      cleanUp("Out of memory");
  110.  
  111.     /* allocation precedence */
  112.     allocIOB->ioa_Request.io_Message.mn_Node.ln_Pri = -40;
  113.  
  114.     allocIOB->ioa_Request.io_Message.mn_ReplyPort = allocPort;
  115.  
  116.     /* allocate from any channel */
  117.     allocIOB->ioa_Data = allocationMap;
  118.     allocIOB->ioa_Length = sizeof(allocationMap);
  119.  
  120.     /* open the audio device with channel allocation and check for errors */
  121.  
  122.     switch (OpenDevice(AUDIONAME, 0, allocIOB, 0)) {
  123.     case IOERR_OPENFAIL:
  124.      cleanUp("Cannot open audio device");
  125.     case ADIOERR_ALLOCFAILED:
  126.      cleanUp("Cannot allocate audio channel");
  127.     }
  128.     device = allocIOB->ioa_Request.io_Device;
  129.  
  130.     /* initialize the software interrupt structure */
  131.  
  132.     if ((interrupt = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),
  133.          MEMF_CLEAR | MEMF_PUBLIC)) == 0)
  134.      cleanUp("Out of memory");
  135.     interrupt->is_Code = (VOID (*)())newBuffer;
  136.  
  137.     /* initialize the reply port for CMD_WRITE's to generate software
  138.        interrupts */
  139.  
  140.     if ((soundPort = (struct MsgPort *)AllocMem(sizeof(struct MsgPort),
  141.          MEMF_CLEAR | MEMF_PUBLIC)) == 0)
  142.      cleanUp("Out of memory");
  143.     soundPort->mp_Flags = PA_SOFTINT;
  144.     soundPort->mp_SigTask = (struct Task *)interrupt;
  145.     soundPort->mp_Node.ln_Type = NT_MSGPORT;
  146.     NewList(&soundPort->mp_MsgList);
  147.  
  148.     /* initialize both I/O blocks for the CMD_WRITES */
  149.  
  150.     for (i = 0; i < 2; ++i) {
  151.  
  152.      /* allocate waveform memory from chip addressable ram.  AllocMem
  153.       * always allocates memory on a word boundary which is necessary
  154.       * for audio waveform data */
  155.      if ((buffer[i] = (BYTE *)AllocMem(BUFFERSIZE, MEMF_CHIP))
  156.           == 0)
  157.          cleanUp("Out of memory");
  158.  
  159.      if ((soundIOB[i] = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),
  160.           MEMF_PUBLIC | MEMF_CLEAR)) == 0)
  161.          cleanUp("Out of memory");
  162.      soundIOB[i]->ioa_Request.io_Message.mn_ReplyPort = soundPort;
  163.      soundIOB[i]->ioa_Request.io_Device = device;
  164.      soundIOB[i]->ioa_Request.io_Unit = allocIOB->ioa_Request.io_Unit;
  165.      soundIOB[i]->ioa_Request.io_Command = CMD_WRITE;
  166.  
  167.      /* load the volume and period registers */
  168.      soundIOB[i]->ioa_Request.io_Flags = ADIOF_PERVOL;
  169.  
  170.      soundIOB[i]->ioa_AllocKey = allocIOB->ioa_AllocKey;
  171.      soundIOB[i]->ioa_Data = buffer[i];
  172.      soundIOB[i]->ioa_Length = BUFFERSIZE;
  173.  
  174.      /* some arbitrary period and volume */
  175.  
  176.      soundIOB[i]->ioa_Period = 200;
  177.      soundIOB[i]->ioa_Volume = 64;
  178.  
  179.      /* play one cycle of each buffer, then reply */
  180.      soundIOB[i]->ioa_Cycles = 1;
  181.  
  182.      /* this really "primes the pump" by causing the reply port
  183.       * to generate a software interrupt and write the first buffers */
  184.      ReplyMsg(soundIOB[i]);
  185.     }
  186.  
  187.     /* wait for CTRL-C to stop the program */
  188.  
  189.     puts("Press CTRL-C to stop");
  190.     Wait(SIGBREAKF_CTRL_C);
  191.  
  192.     /* free up resources and exit */
  193.     cleanUp("");
  194. }
  195.  
  196.  
  197. /* print an error message and free allocated resources */
  198.  
  199. cleanUp(message)
  200. TEXT *message;
  201. {
  202.     int i;
  203.  
  204.     puts(message);
  205.     if (device != 0)
  206.  
  207.      /* CloseDevice'ing with 'allocIOB' preforms an ADCMD_FREE on any
  208.       * channel allocated with 'allocIOB's ioa_AllocKey.  ADCMD_FREE
  209.       * performs a CMD_RESET, which performs a CMD_FLUSH, which AbortIO's
  210.       * any CMD_WRITES to those channels */
  211.      CloseDevice(allocIOB);
  212.  
  213.     for (i = 0; i < 2; ++i) {
  214.      if (soundIOB[i])
  215.          FreeMem(soundIOB[i], sizeof(struct IOAudio));
  216.      if (buffer[i])
  217.          FreeMem(buffer[i], BUFFERSIZE);
  218.     }
  219.     if (soundPort)
  220.      FreeMem(soundPort, sizeof(struct MsgPort));
  221.     if (interrupt)
  222.      FreeMem(interrupt, sizeof(struct Interrupt));
  223.     if (allocIOB)
  224.      FreeMem(allocIOB, sizeof(struct IOAudio));
  225.     if (allocPort)
  226.      DeletePort(allocPort, sizeof(struct MsgPort));
  227.     if (MathBase)
  228.      CloseLibrary(MathBase);
  229.     exit();
  230. }
  231.  
  232.  
  233. /* software interrupt server code */
  234.  
  235. newBuffer()
  236. {
  237.     int i;
  238.     struct IOAudio *ioa;
  239.     BYTE *buffer;
  240.     BYTE mouseChange, curYMouse;
  241.     ULONG newFreq;
  242.  
  243.     /* get I/O block from reply port */
  244.     ioa = (struct IOAudio *)GetMsg(soundPort);
  245.  
  246.     /* check if there really was an I/O blocks on the port and if there
  247.      * are no errors.  An error would indicate either the channel was
  248.      * aborted from being stolen (IOERR_ABORTED), it stolen before the
  249.      * write was performed and had the wrong allocation key
  250.      * (ADIOF_NOALLOCATION), or it was aborted by being CloseDevice'd
  251.      * In any case if there is an error do not send the next write.  The
  252.      * program will just wait around silently */
  253.     if (ioa && ioa->ioa_Request.io_Error == 0) {
  254.  
  255.      /* determine how far the mouse has moved */
  256.  
  257.      curYMouse = YMOUSEREG;
  258.      mouseChange = curYMouse - lastYMouse;
  259.      lastYMouse = curYMouse;
  260.  
  261.      /* modify the frequency proportionally */
  262.      newFreq = frequency + mouseChange * (frequency >> 6);
  263.  
  264.      /* limit the frequency range */
  265.      if (newFreq > 0x800000 && newFreq < 0x40000000) 
  266.          frequency = newFreq;
  267.  
  268.      /* scan the table and copy each new sample into the audio waveform
  269.       * buffer */
  270.  
  271.      for (i = 0, buffer = ioa->ioa_Data; i < BUFFERSIZE; ++i)
  272.          *buffer++ = sineTable[(angle += frequency) >>
  273.               (32 - SINETABLEPOWER2)];
  274.  
  275.      /* send the write I/O block */
  276.      BeginIO(ioa);
  277.  
  278.     }
  279. }
  280.  
  281.